package com.cwx.mychat.socketio;

import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.OnConnect;
import com.corundumstudio.socketio.annotation.OnDisconnect;
import com.corundumstudio.socketio.annotation.OnEvent;
import com.cwx.mychat.entity.enums.MessageTypeEnum;
import com.cwx.mychat.entity.po.UserInfo;
import com.cwx.mychat.entity.vo.MessageSendVo;
import com.cwx.mychat.service.UserInfoService;
import com.cwx.mychat.websocket.message.SendMessageHandler;
import io.netty.handler.codec.http.HttpHeaders;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 作者： cwx
 * 时间： 2025/2/13 14:21
 * 版本： 1.0
 * 注释：
 */
@Slf4j
@Component("socketIOMessageEventHandler")
public class MessageEventHandler {

    @Resource
    private SocketIOServer socketIOServer;

    @Autowired
    private SendMessageHandler sendMessageHandler;

    @Resource
    private UserInfoService userInfoService;

    public static ConcurrentMap<String, SocketIOClient> socketIOClientMap = new ConcurrentHashMap<>();

    public static ConcurrentMap<String, ScheduledExecutorService> socketIOScheduler = new ConcurrentHashMap<>();

    //房间里面的用户
    public static ConcurrentMap<String, HashSet<String>> roomUserMap = new ConcurrentHashMap<>();

    /**
     * 客户端连接时触发
     * @param client
     */
    @OnConnect
    public void onConnect(SocketIOClient client) {
        log.info("连接到客户端");
        //获取请求头中的token
        HttpHeaders httpHeaders = client.getHandshakeData().getHttpHeaders();
        String token = httpHeaders.get("Authorization");
        if(token == null){
            //断开连接
            log.info("token为空，断开连接");
            client.disconnect();
        }
//        socketIOClientMap.put(token, client);
    }

    /**
     * 客户端关闭连接时触发
     */
    @OnDisconnect
    public void close(SocketIOClient client) {
        log.info("客户端{}断开连接", client.getSessionId());
        client.disconnect();
    }

    /**
     * 加入房间
     * @param client
     * @param ackRequest
     */
    @OnEvent("joinRoom")
    public void joinRoom(SocketIOClient client, AckRequest ackRequest, JoinRoomData joinRoomData){
        //加锁，防止有重复加入,使用安全锁ReenterLock
        ReentrantLock lock = new ReentrantLock();
        lock.lock();
        if(roomUserMap.containsKey(joinRoomData.roomId)){
            roomUserMap.get(joinRoomData.roomId).add(joinRoomData.userId);
        }else{
            roomUserMap.put(joinRoomData.roomId, new LinkedHashSet<>(Collections.singletonList(joinRoomData.userId)));
        }
        System.out.println("客户端"+client.getSessionId()+"加入房间"+joinRoomData.roomId);
        client.joinRoom(joinRoomData.roomId);
        lock.unlock();
    }

    /**
     * 打电话
     * @param client
     * @param ackRequest
     */
    @OnEvent("callRemote")
    public void callRemote(SocketIOClient client, AckRequest ackRequest, String roomId){
        System.out.println("客户端"+client.getSessionId()+"调用房间"+roomId);
        String contactId = client.getHandshakeData().getSingleUrlParam("contactId");
        String userId = client.getHandshakeData().getSingleUrlParam("userId");
        String sessionId = client.getHandshakeData().getSingleUrlParam("sessionId");
        Boolean audio = Boolean.parseBoolean(client.getHandshakeData().getSingleUrlParam("audio"));
        UserInfo userInfo = userInfoService.getById(userId);
        if(socketIOScheduler.get(roomId) == null || socketIOScheduler.get(roomId).isShutdown()){
            ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
            socketIOScheduler.put(roomId, scheduler);
        }
        //定义一个任务每隔一秒进行调用
        Runnable task = () -> {
            log.info("发送事件到房间{},contactId = {}, userId = {}, sessionId = {}", roomId, contactId, userId, sessionId);
            //发送消息给联系人
            MessageSendVo<Map<String, Object>> messageSendVo = new MessageSendVo<>();
            messageSendVo.setContactId(contactId);
            messageSendVo.setMessageType(MessageTypeEnum.VIDEO_CALL.getType());
            messageSendVo.setSendUserId(userId);
            messageSendVo.setSessionId(sessionId);
            //查询UserInfo
            messageSendVo.setAvatar(userInfo.getUserAvatar());
            messageSendVo.setSendUserNickName(userInfo.getNickName());
            HashMap<String, Object> map = new HashMap<>();
            map.put("audio", audio);
            map.put("roomId", roomId);
            messageSendVo.setExtendData(map);
            sendMessageHandler.sendMessage(messageSendVo);
            ackRequest.sendAckData("完成任务");
        };
        //每隔一秒执行一次，超时30秒结束任务
        socketIOScheduler.get(roomId).scheduleAtFixedRate(task, 0, 2, TimeUnit.SECONDS);
        // 设置超时任务，30秒后结束调度
        socketIOScheduler.get(roomId).schedule(() -> {
            System.out.println("结束任务");
            socketIOScheduler.get(roomId).shutdownNow(); // 结束调度
            //发送超时通知
            socketIOServer.getRoomOperations(roomId).sendEvent("timeout");
        }, 30, TimeUnit.SECONDS);
    }


    @OnEvent("stopVideo")
    public void stopVideo(SocketIOClient client, AckRequest ackRequest, String roomId){
        System.out.println("客户端"+client.getSessionId()+"结束请求任务"+roomId);
        socketIOScheduler.get(roomId).shutdown();
        ackRequest.sendAckData("完成任务");
    }

    /**
     * 接受打电话
     * @param client
     * @param ackRequest
     */
    @OnEvent("acceptCall")
    public void acceptCall(SocketIOClient client, AckRequest ackRequest,String roomId){
        System.out.println("客户端"+client.getSessionId()+"接受房间"+roomId);
        socketIOScheduler.get(roomId).shutdownNow();
        socketIOServer.getRoomOperations(roomId).sendEvent("acceptCall");
    }

    /**
     * 接受客户端的offer
     * @param client
     * @param ackRequest
     * @param message
     */
    @OnEvent("clientSendOffer")
    public void clientSendOffer(SocketIOClient client, AckRequest ackRequest, OfferData message){
        System.out.println("收到offer"+client.getSessionId()+"发送房间"+message.roomId+"的消息");
        socketIOServer.getRoomOperations(message.roomId).sendEvent("ServerSendOffer",message.offer);
    }

    /**
     * 接受客户端的answer
     * @param client
     * @param ackRequest
     * @param message
     */
    @OnEvent("clientSendAnswer")
    public void clientSendAnswer(SocketIOClient client, AckRequest ackRequest, AnswerData message){
        System.out.println("收到answer"+client.getSessionId()+"发送房间"+message.roomId+"的消息");
        socketIOServer.getRoomOperations(message.roomId).sendEvent("ServerSendAnswer",message.answer);
    }

    /**
     * 接受客户端的 candidate
     * @param client
     * @param ackRequest
     * @param message
     */
    @OnEvent("clientSendCandidate")
    public void clientSendCandidate(SocketIOClient client, AckRequest ackRequest, CandidateData message){
        System.out.println("收到candidate"+client.getSessionId()+"发送房间"+message.roomId+"的消息"+ message.candidate);
        socketIOServer.getRoomOperations(message.roomId).sendEvent("ServerSendCandidate",message);
    }

    /**
     * 接受客户端的挂断请求
     * @param client
     * @param ackRequest
     * @param message
     */
    @OnEvent("clientHangUp")
    public void clientHangUp(SocketIOClient client, AckRequest ackRequest, HangUpData message){
        System.out.println("收到挂断请求"+client.getSessionId()+"发送房间"+message.roomId+"的消息");
        socketIOScheduler.get(message.roomId).shutdownNow();
        socketIOServer.getRoomOperations(message.roomId).sendEvent("ServerHangUp",message.status,message.called);
    }

    /**
     * 广播消息
     * @param message
     */
    public void sendBroadcastMessage(String message) {
        for(SocketIOClient client : socketIOClientMap.values()) {
            if(client.isChannelOpen()) {
                client.sendEvent("broadcast", message);
            }
        }
    }

    public record JoinRoomData(String userId, String roomId) {}

    public record OfferData(String offer, String roomId) {}

    public record AnswerData(String answer, String roomId) {}

    public record CandidateData(String candidate, String roomId, String userId) {}

    public record HangUpData (String roomId, String status, String called){}
}
